package com.geor.gcv.core.extract;

import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import com.geor.gcv.core.mat.Mats;
import com.geor.gcv.core.model.Area;
import com.geor.gcv.core.process.GrayProcess;

/**
 * 矩形提取
 * 
 * @author geor.lusongnan
 *
 */
public class RectExtractor extends AbstractExtractor {
	private List<Area> areas;
	/** 膨胀参数 */
	private Size size = new Size(10, 10);

	public void setSize(Size size) {
		this.size = size;
	}

	@Override
	public void execute(Mat mat) {
		try {
			_execute(mat);
		} finally {
			Mats.releseMat(mat);
		}
	}

	protected void _execute(Mat mat) {
		Mat gray = new Mat();
		if (mat.channels() != 1) {
			gray = new GrayProcess(mat).process();
		} else {
			gray.copyTo(mat);
		}
		areas = new ArrayList<Area>();
		Mat pz = new Mat();
		Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, size);
		Imgproc.morphologyEx(gray, pz, Imgproc.MORPH_CLOSE, element);

		Mat edges = new Mat();
		Imgproc.Canny(pz, edges, 100, 150);
		List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
		Mat hierarchy = new Mat();
		// System.out.println("rects:" + edges);
		Imgproc.findContours(edges, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_NONE);
		for (MatOfPoint mp : contours) {
			Rect rect = Imgproc.boundingRect(mp);
			// 过滤一下区域
			Area area = new Area().toAreaByRect(rect);
			if (area == null)
				continue;
			if (area.getWidth() < mat.cols() * 0.1 && area.getHeight() < mat.rows() * 0.1) {
				continue;
			}
			areas.add(area);
			Mats.releseMat(mp);
		}
		// 合并重合区域
		Mats.coincide(areas);
		// 查找中心区域
		Area area = findCenter(mat, areas);
		Area maxArea = findAreaMax(areas);
		areas.clear();
		if (area != null && maxArea != null) {
			if (area.equals(maxArea)) {
				areas.add(area);
			} else {
				Area tmp = Mats.mergerArea(area, maxArea);
				areas.add(tmp);
			}
		}
	}

	private Area findAreaMax(List<Area> areas) {
		if (areas == null || areas.size() == 0) {
			return null;
		}
		if (areas.size() == 1) {
			return areas.get(0);
		}
		Area area = areas.get(0);
		for (int i = 0; i < areas.size(); i++) {
			if (area.getArea() < areas.get(i).getArea()) {
				area = areas.get(i);
			}
		}
		return area;
	}

	// 找到最中心的区域
	private Area findCenter(Mat src, List<Area> areas) {
		Point matCenter = new Point(src.cols() / 2, src.rows() / 2);
		double distanceMAX = src.cols() + src.rows();
		int rectIndex = -1;

		for (int i = 0; i < areas.size(); i++) {
			Area rect = areas.get(i);
			Point rectCenter = new Point(rect.getX() + rect.getWidth() / 2, rect.getY() + rect.getHeight() / 2);
			double distance = Math.pow((matCenter.x - rectCenter.x) * (matCenter.x - rectCenter.x)
					+ (matCenter.y - rectCenter.y) * (matCenter.y - rectCenter.y), 0.5);
			if (distance < distanceMAX) {
				distanceMAX = distance;
				rectIndex = i;
			}
		}

		if (rectIndex >= 0) {
			return areas.get(rectIndex);
		}
		return null;
	}

	@Override
	public List<Area> get() {
		return areas;
	}

}
